#include "MessagePartNetworkReply.h"
#include "3rdParty/trojita/Imap/Model/ItemRoles.h"
#include "3rdParty/trojita/Imap/Model/MailboxTree.h"
#include "3rdParty/trojita/Imap/Model/Model.h"
#include "3rdParty/trojita/Imap/Encoders.h"
#include "3rdParty/trojita/UiUtils/Formatting.h"

namespace Dekko
{

namespace Network
{

MessagePartNetworkReply::MessagePartNetworkReply(MsgPartNetworkAccessManager *parent, const QPersistentModelIndex &part, bool requireFormatting):
    QNetworkReply(parent), part(part), formattedBufferContent(0), requireFormatting(requireFormatting)
{
    QUrl url;
    url.setScheme(QStringLiteral("dekko-imap"));
    url.setHost(QStringLiteral("msg"));
    url.setPath(part.data(Imap::Mailbox::RolePartPathToPart).toString());
    setUrl(url);

    setOpenMode(QIODevice::ReadOnly | QIODevice::Unbuffered);
    Q_ASSERT(part.isValid());
    const Imap::Mailbox::Model *model = 0;
    Imap::Mailbox::Model::realTreeItem(part, &model);
    Q_ASSERT(model);

    connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotModelDataChanged(QModelIndex,QModelIndex)));

    //TODO: fileDownloadProgress signal in model signal the process of the current download.
    //      This reply might not be the current download, but now we assume that because the only use case here
    //      is just see the download progress of attachments that is usually the only downloading event.
    //      Should match message UID and partId and then emit downloadProgress.
    connect(model, SIGNAL(fileDownloadProgress(qint64, qint64)), this, SIGNAL(downloadProgress(qint64,qint64)));

    Imap::Mailbox::TreeItemPart *partPtr = dynamic_cast<Imap::Mailbox::TreeItemPart *>(static_cast<Imap::Mailbox::TreeItem *>(part.internalPointer()));
    Q_ASSERT(partPtr);

    // We have to ask for contents before we check whether it's already fetched
    partPtr->fetch(const_cast<Imap::Mailbox::Model *>(model));
    // The part data might be already unavailable or already fetched
    QTimer::singleShot(0, this, SLOT(slotMyDataChanged()));

    buffer.setBuffer(partPtr->dataPtr());
    buffer.open(QIODevice::ReadOnly);
}

/** @short Check to see whether the data which concern this object has arrived already */
void MessagePartNetworkReply::slotModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
    Q_UNUSED(bottomRight);
    // FIXME: use bottomRight as well!
    if (topLeft.model() != part.model()) {
        return;
    }
    if (topLeft == part) {
        slotMyDataChanged();
    }
}

/** @short Data for the current message part are available now */
void MessagePartNetworkReply::slotMyDataChanged()
{
    if (part.data(Imap::Mailbox::RoleIsUnavailable).toBool()) {
        setError(TimeoutError, tr("Offline"));
        setFinished(true);
        emit error(TimeoutError);
        emit finished();
        return;
    }

    if (!part.data(Imap::Mailbox::RoleIsFetched).toBool())
        return;

    MsgPartNetworkAccessManager *netAccess = qobject_cast<MsgPartNetworkAccessManager*>(manager());
    Q_ASSERT(netAccess);
    QString mimeType = netAccess->translateToSupportedMimeType(part.data(Imap::Mailbox::RolePartMimeType).toString());
    QString charset = part.data(Imap::Mailbox::RolePartCharset).toString();
    if (mimeType == QLatin1String("text/plain") && requireFormatting) {
        QString decodedString = Imap::decodeByteArray(buffer.data(), charset);
        decodedString = UiUtils::Formatting::markUpPlainTextInQML(decodedString);

        setHeader(QNetworkRequest::ContentTypeHeader, "text/html");
        formattedBufferContent = new QByteArray(decodedString.toUtf8().data());
        buffer.close();
        buffer.setBuffer(formattedBufferContent);
        buffer.open(QBuffer::ReadOnly);
    } else {
        setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
    }
    setFinished(true);
    emit readyRead();
    emit finished();
}

/** @short QIODevice compatibility */
void MessagePartNetworkReply::abort()
{
    close();
}

/** @short QIODevice compatibility */
void MessagePartNetworkReply::close()
{
    disconnectBufferIfVanished();
    if (formattedBufferContent) {
        delete formattedBufferContent;
        formattedBufferContent = 0;
    }
    buffer.close();
}

/** @short QIODevice compatibility */
qint64 MessagePartNetworkReply::bytesAvailable() const
{
    disconnectBufferIfVanished();
    return buffer.bytesAvailable() + QNetworkReply::bytesAvailable();
}

/** @short QIODevice compatibility */
qint64 MessagePartNetworkReply::readData(char *data, qint64 maxSize)
{
    disconnectBufferIfVanished();
    return buffer.read(data, maxSize);
}


/** @short Cut the buffer connection in case the message got removed */
void MessagePartNetworkReply::disconnectBufferIfVanished() const
{
    if (!part.isValid()) {
        buffer.close();
        buffer.setBuffer(0);
    }
}

}
}
